using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sedna.Core.Controller.Configurations;
using Sedna.Core.Controls.Support;
using Sedna.Core.Persistence;
using Sedna.Core.Support;
using Sedna.DAO;
using Spring.Util;

namespace Sedna.Core.Controller
{
    public abstract class ListController : BaseController, IListController
    {        
        protected const String CACHE_KEY = "dataSource";

        private bool autoAttach = true;
        private IBaseDAO objectDAO;

        protected IDataProvider lastUsedDataProvider = null;

        private IEqualityComparer elementsComparer;

        protected bool controlBinded = true; // Have to use because the page loads twice 
        
        private IDataProvider dataProvider;
        private bool cacheData;

        private String cacheDataPath;

        # region Properties

        public virtual new IListControl Control
        {
            get { return (IListControl)base.Control; }
            set
            {
                if (base.Control != value &&
                    base.Control != null &&
                    base.Control is IControlBindingAware)
                {
                    (base.Control as IControlBindingAware).AfterBinding -= new BindingHandler(Control_AfterBinding);
                    (base.Control as IControlBindingAware).AfterUnbinding -= new BindingHandler(Control_AfterUnbinding);
                }

                base.Control = value as Control;

                if (base.Control != null &&
                    base.Control is IControlBindingAware)
                {
                    (base.Control as IControlBindingAware).AfterBinding += new BindingHandler(Control_AfterBinding);
                    (base.Control as IControlBindingAware).AfterUnbinding += new BindingHandler(Control_AfterUnbinding);
                }
            }
        }

        public IEqualityComparer ElementsComparer
        {
            get { return elementsComparer; }
            set { elementsComparer = value; }
        }

        public virtual bool CacheData
        {
            get { return cacheData; }
            set { cacheData = value; }
        }

        public bool AutoAttach
        {
            get { return autoAttach; }
            set { autoAttach = value; }
        }

        public IBaseDAO ObjectDAO
        {
            set { objectDAO = value; }
        }

        public IDataProvider DataProvider
        {
            get { return dataProvider; }
            set { dataProvider = value; }
        }

        public override IDataProvider CurrentDataProvider
        {
            get
            {
                if (base.CurrentDataProvider == null)
                {
                    base.CurrentDataProvider = dataProvider; 
                }
                
                return base.CurrentDataProvider;
            }
            set { base.CurrentDataProvider = value; }
        }
        
        /// <summary>
        /// Make sense only when data caching is turned on
        /// </summary>
        public IList Items
        {
            get { return new ArrayList(ItemsCache.Values); }
        }

        public String CacheDataPath
        {
            set { cacheDataPath = value; }
        }

        # endregion

        # region Public Methods

        public override void LoadOrRefresh()
        {
            if (NewDataProvider != null)
            {
                LoadNewData();
            }
            else
            {
                Refresh();
            }
        }

        public override void LoadNewData(IDataProvider dataProvider)
        {
            Assert.IsNotNull(dataProvider, "Data provider must be not null");

            CurrentDataProvider = (IDataProvider)dataProvider.Clone();
            CurrentDataProvider.StartIndex = 0;

            BindControl(CurrentDataProvider);
        }

        # endregion

        protected IDictionary ItemsCache
        {
            get
            {
                if (!String.IsNullOrEmpty(cacheDataPath))
                {
                    return GetDictionaryByPath(cacheDataPath);
                }
                
                return GetDictionaryFromContext(CACHE_KEY);
            }
        }
        
        protected bool IsCacheEmpty
        {
            get { return ItemsCache.Count == 0; }
        }
        
        protected void AttachCacheToSession()
        {
            if (autoAttach)
                objectDAO.AttachToSession(Items);
        }

        protected override void BindControl(IDataProvider dataProvider)
        {
            if (controlBinded)
                return;

            controlBinded = true;

            ICollection data;

            if (CacheData)
            {
                if (NewDataProvider == dataProvider || IsCacheEmpty
                   /* (!String.IsNullOrEmpty(cacheDataPath) && IsCacheEmpty) ||
                    (String.IsNullOrEmpty(cacheDataPath) && !(Control as Control).Page.IsPostBack)*/)
                {
                    // New data provider has been found or this is the first page load
                    data = new ArrayList(dataProvider.LoadData());
                }
                else
                {
                    AttachCacheToSession();
                    
                    data = new ArrayList(ItemsCache.Values);                    
                }
            }
            else // no caching
            {
                if (lastUsedDataProvider == null || !lastUsedDataProvider.Equals(dataProvider) || IsCacheEmpty)
                {
                    // Data have not been loaded on 'Unbind' step or previous used provider became out of date
                    data = new ArrayList(dataProvider.LoadData());
                }
                else
                {
                    AttachCacheToSession();
                    
                    // Use data that have been loaded on 'Unbind' step
                    data = new ArrayList(ItemsCache.Values);
                }
            }


            Control.DataSource = data;
            Control.DataBind();

            ItemsCache.Clear();

            // Extension point
            CustomBind(data);

            if (!CacheData)
                ItemsCache.Clear();
        }

        protected abstract void CustomBind(ICollection data);

        protected abstract void Control_AfterBinding(Control control);
        
        protected abstract void Control_AfterUnbinding(Control control);
    }
}
